1 /*
2 * Copyright (C) 2013 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15 package com.google.common.hash;
16
17 import static com.google.common.base.Preconditions.checkNotNull;
18
19 import com.google.common.annotations.Beta;
20
21 import java.io.FilterInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 /**
26 * An {@link InputStream} that maintains a hash of the data read from it.
27 *
28 * @author Qian Huang
29 * @since 16.0
30 */
31 @Beta
32 public final class HashingInputStream extends FilterInputStream {
33 private final Hasher hasher;
34
35 /**
36 * Creates an input stream that hashes using the given {@link HashFunction} and delegates all data
37 * read from it to the underlying {@link InputStream}.
38 *
39 * <p>The {@link InputStream} should not be read from before or after the hand-off.
40 */
41 public HashingInputStream(HashFunction hashFunction, InputStream in) {
42 super(checkNotNull(in));
43 this.hasher = checkNotNull(hashFunction.newHasher());
44 }
45
46 /**
47 * Reads the next byte of data from the underlying input stream and updates the hasher with
48 * the byte read.
49 */
50 @Override
51 public int read() throws IOException {
52 int b = in.read();
53 if (b != -1) {
54 hasher.putByte((byte) b);
55 }
56 return b;
57 }
58
59 /**
60 * Reads the specified bytes of data from the underlying input stream and updates the hasher with
61 * the bytes read.
62 */
63 @Override
64 public int read(byte[] bytes, int off, int len) throws IOException {
65 int numOfBytesRead = in.read(bytes, off, len);
66 if (numOfBytesRead != -1) {
67 hasher.putBytes(bytes, off, numOfBytesRead);
68 }
69 return numOfBytesRead;
70 }
71
72 /**
73 * mark() is not supported for HashingInputStream
74 * @return {@code false} always
75 */
76 @Override
77 public boolean markSupported() {
78 return false;
79 }
80
81 /**
82 * mark() is not supported for HashingInputStream
83 */
84 @Override
85 public void mark(int readlimit) {}
86
87 /**
88 * reset() is not supported for HashingInputStream.
89 * @throws IOException this operation is not supported
90 */
91 @Override
92 public void reset() throws IOException {
93 throw new IOException("reset not supported");
94 }
95
96 /**
97 * Returns the {@link HashCode} based on the data read from this stream. The result is
98 * unspecified if this method is called more than once on the same instance.
99 */
100 public HashCode hash() {
101 return hasher.hash();
102 }
103 }